<template>
  <div class="employees-container">
    <div class="app-container">
      <!-- 通用工具栏组件使用 -->
      <page-tools>
        <!-- 自定义左侧内容 -->
        <template #slot-left>
          <span>共19条记录</span>
        </template>

        <!-- 自定义右侧内容 -->
        <template #slot-right>
          <el-button v-power="'import'" type="danger" size="small" @click="uploadExcelClickFn">导入excel</el-button>
          <el-button v-power="'export'" type="success" size="small" @click="exportExcelClickFn">导出excel</el-button>
          <el-button type="primary" size="small" @click="addEmpClickFn">新增员工</el-button>
        </template>
      </page-tools>

      <el-card style="margin-top: 10px;">
        <el-table border :data="employeesList" :default-sort="{prop: 'timeOfEntry', order: 'descending'}">
          <el-table-column label="序号" type="index" />
          <el-table-column label="姓名" prop="username" />
          <el-table-column label="头像" prop="staffPhoto">
            <template v-slot="{ row }">
              <image-holder :src="row.staffPhoto" />
            </template>
          </el-table-column>
          <el-table-column label="手机号" prop="mobile" />
          <el-table-column label="工号" prop="workNumber" />
          <el-table-column label="聘用形式" prop="formOfEmployment" :formatter="formOfEmploymentFormatterFn" sortable />
          <el-table-column label="部门" prop="departmentName" />
          <el-table-column label="入职时间" prop="timeOfEntry" sortable :sort-method="timeOfEntrySortFn">
            <template v-slot="scope">
              {{ scope.row.timeOfEntry | formatTime }}
            </template>
          </el-table-column>

          <el-table-column label="操作" width="280">
            <template v-slot="scope">
              <el-button type="text" size="small" @click="lookDetailFn(scope.row.id)">查看</el-button>
              <el-button type="text" size="small" @click="assignRoleFn(scope.row.id)">分配角色</el-button>
              <el-button type="text" size="small" @click="delEmpFn(scope.row.id)">删除</el-button>
            </template>
          </el-table-column>
        </el-table>

        <!-- 分页组件 -->
        <el-row type="flex" justify="center" align="middle" style="height: 60px">
          <!-- 分页区域 -->
          <el-pagination :current-page="query.page" :page-sizes="[10, 15, 20, 25]" :page-size="query.size" layout="total, sizes, prev, pager, next, jumper" :total="total" @size-change="handleSizeChange" @current-change="handleCurrentChange" />
        </el-row>
      </el-card>

      <!-- 新增员工-弹窗 -->
      <el-dialog title="新增员工" :visible.sync="showDialog" @close="closeDialogFn">
        <emp-dialog ref="empDialog" :show-dialog.sync="showDialog" :departments-tree-list="departmentsTreeList" @addEmpEV="addEmpFn" />
      </el-dialog>

      <!-- 角色分配-弹窗 -->
      <el-dialog title="角色分配" :visible.sync="roleShowDialog">
        <assign-role ref="assignRole" v-model="roleShowDialog" :role-list="roleList" @saveRoleIdsEV="saveRoleFn" />
      </el-dialog>

    </div>
  </div>
</template>

<script>
import {
  getEmployeesListAPI,
  delEmployeesAPI,
  addEmployeesAPI,
  getRoleListAPI,
  getUserDetailByAPI,
  saveRoleIdsAPI
} from '@/api/employees'

import empDialog from './empDialog.vue' // el-form表单组件封装在内

import EmployeeEnum from '@/api/constant/index'

import { getDepartmentsListAPI } from '@/api/dapartments'
import { tranListToTreeData } from '@/utils'

import dayjs from 'dayjs'

import assignRole from './assignRole.vue'

export default {
  name: 'Employees',
  components: {
    empDialog,
    assignRole
  },
  filters: {
    // 格式化入职日期的方法
    formatTime(time) {
      return dayjs(time).format('YYYY-MM-DD')
    }
  },
  data() {
    return {
      query: {
        page: 1, // 页码
        size: 10 // 每页条数
      },
      employeesList: [], // 员工列表
      total: 0, // 数据总条数
      showDialog: false, // 新增员工-弹窗是否出现
      departmentsTreeList: [], // 部门列表(树形数据)
      roleShowDialog: false, // 角色分配-弹窗是否出现
      roleList: [], // 所有角色列表
      clickEmpId: '' // 正在分配角色的员工id
    }
  },
  created() {
    this.getEmployeesListFn()
    // const list = [1, 5, 3, 7, 0, 2, 4]
    // const result = list.sort((a, b) => a - b)
    // console.log(result)
    this.getDepartmentsListAPI()
    this.getRoleListFn()
  },
  methods: {
    // 获取员工列表
    async getEmployeesListFn() {
      const res = await getEmployeesListAPI(this.query)
      // console.log(res)

      // 将返回的结果赋值 data 中的数据
      this.employeesList = res.data.rows
      this.total = res.data.total
    },
    // 每页显示的条数发生改变时触发
    handleSizeChange(newSize) {
      this.query.size = newSize
      this.getEmployeesListFn()
    },

    // 当前页面发生改变时触发
    handleCurrentChange(newPage) {
      this.query.page = newPage
      this.getEmployeesListFn()
    },
    // 格式化->聘用形式列内容
    formOfEmploymentFormatterFn(row) {
      // row: 其实就是scope.row->行的数据对象
      const obj = EmployeeEnum.hireType.find(item => item.id === row.formOfEmployment * 1)
      // console.log(obj)
      // 找到了用value的值, 找不到就返回'未知'字符串给列内容
      return obj ? obj.value : '未知'
    },
    // 入职时间->排序
    timeOfEntrySortFn(a, b) {
      // 还要和数组的sort的表现一致, 必须return一个数字 (小于0, a在前, b在后, 等于0不动, 大于0, b在前, a在后)
      // a和b是对象不能直接-, 但是取出来了日期字符串(因为中间有-), 所以这个日期字符串也不能做减法(硬减直接是NaN)
      // return NaN相当于return 了0, 所有顺序压根没动
      // 还有可能是前端直接把日期对象硬转成了字符串"2022-01-31T16:00:00.000Z"
      // 所以不用思考字符串之间的比较了
      // 解决: 试着转成日期对象, 算毫秒进行对比
      // new Date() 有要求, 传入的年月日, 是年/月/日的字符串, 是年-月-日字符串也可以
      // 但是数据不一致性, undefined我们就给它弄个1970-01-01日(算最小的)
      // console.log(a.timeOfEntry, b.timeOfEntry)

      if (!a.timeOfEntry) {
        a.timeOfEntry = '1970-01-01'
      }
      if (!b.timeOfEntry) {
        b.timeOfEntry = '1970-01-01'
      }

      const aDate = new Date(a.timeOfEntry)
      const bDate = new Date(b.timeOfEntry)

      return aDate.getTime() - bDate.getTime()
    },
    // 删除->员工->点击事件
    async delEmpFn(id) {
      await delEmployeesAPI(id)

      if (this.employeesList.length === 1) {
        this.query.page--
        if (this.query.page <= 0) {
          this.query.page = 1
        }
      }

      this.getEmployeesListFn() // 刷新页面,重新获取数据
    },
    // 新增员工-按钮-点击事件(头部工具栏右上角按钮)
    addEmpClickFn() {
      this.showDialog = true
    },
    // 获取部门列表
    async getDepartmentsListAPI() {
      const res = await getDepartmentsListAPI()
      // console.log(res)
      this.departmentsTreeList = tranListToTreeData(res.data.depts, '')
    },
    // 新增员工->表单确定事件
    async addEmpFn(formObj) {
      // addEmployeesAPI() -> 返回在原地的是一个axios函数调用后在原地的一个Promise对象
      // Promise对象内包裹着一个ajax异步任务请求
      // ajax异步成功->Promise对象改为成功状态(把要执行then()里函数体代码/await这样, 推到微任务队列里, JS执行栈就执行await这一行代码了)

      // Promise对象可以.then()拿到成功(Promise内resolve()的结果
      // 也可以使用await取代.then(), 直接把后台返回的成功的结果留在原地

      // await不能"直接"拿到失败的结果
      // 解决: try+catch捕获 / .catch(err => err): 链式调用特性
      // try {} catch(err) {}

      // 因为date-picker绑定的是日期对象, 但是传入给axios会全都试着转成JSON字符串, 所以格式就不太对, 所以需要我们自己提前在前端把日期对象转成年-月-日的日期格式字符串
      // 自己写: 思路, 拿到日期对象的年数字, 月数字, 日数字, 前面补0拼接字符串
      // 借助包: dayjs (专门处理时间的各种方法)
      const result = dayjs(formObj.timeOfEntry).format('YYYY-MM-DD')
      formObj.timeOfEntry = result

      const res = await addEmployeesAPI(formObj)
      console.log(res)
    },
    // 弹窗+表单关闭事件
    closeDialogFn() {
      // 表单重置
      // this.$refs.empDialog // empDialog组件对象(右侧this的值)
      // 右侧this.$refs.addForm // el-form组件对象
      this.$refs.empDialog.$refs.addForm.resetFields()
    },
    // 导入excel-按钮->点击事件
    uploadExcelClickFn() {
      this.$router.push('/excel')
    },
    // 导出excel-按钮->点击事件
    async exportExcelClickFn() {
      const res = await getEmployeesListAPI({
        page: 1,
        size: this.total
      })
      console.log(res)
      const empList = res.data.rows // 员工数组列表

      // 考虑顺序 (页面上列保持一致顺序)
      // 4.0 准备一个列表头中文数组 (8个)
      const headerArr = ['序号', '姓名', '头像', '手机号', '工号', '聘用形式', '部门', '入职时间']
      // 4.1 数据key的映射关系(思路: 遍历上边数组里按照顺序, 取出中文的名字, 但是对应数组值的数组, 要从英文key对象中取出value值, 我要用中文key换到英文key然后去取到值)
      const myObj = {
        // 序号可以等遍历的时候直接用索引值, 而不是来自于英文对象里
        姓名: 'username',
        头像: 'staffPhoto',
        手机号: 'mobile',
        工号: 'workNumber',
        聘用形式: 'formOfEmployment',
        部门: 'departmentName',
        入职时间: 'timeOfEntry'
      }
      // 4.2 按照顺序, 形成值的数组 (明确目标->想要数据结构->读代码(每个变量意思)读2-3遍->仿写3-5遍)
      const resultArr = empList.map((item, index) => {
        const valueArr = [] // 值小数组
        headerArr.forEach(zhKey => {
          if (zhKey === '序号') {
            valueArr.push(index + 1)
          } else {
            const enKey = myObj[zhKey]
            valueArr.push(item[enKey])
          }
        })
        return valueArr
      })

      // const enKeyArr = Object.keys(empList[0])
      // const zhKeyArr = enKeyArr.map(enKey => {
      //   return myObj[enKey]
      // })
      // console.log(zhKeyArr)

      // const valueArr = empList.map(obj => {
      //   return Object.values(obj)
      // })

      import('@/vendor/Export2Excel').then(excel => {
        excel.export_json_to_excel({
          header: headerArr, // 表格列表头
          data: resultArr,
          filename: '人资项目-员工导出', // 文件名
          autoWidth: true, // 自动宽度
          bookType: 'xlsx' // xlsx文件类型
        })
      })
    },
    // 点击查看->跳转员工详情
    lookDetailFn(empId) {
      this.$router.push(`/employees/detail?id=${empId}`)
    },
    // 分配角色->按钮点击事件
    async assignRoleFn(empId) {
      // empId: 员工ID
      this.roleShowDialog = true // 分配角色弹窗出现

      this.clickEmpId = empId

      // 请求这个员工的基本信息(包含默认角色列表信息)
      const res = await getUserDetailByAPI(empId)
      console.log(res)
      this.$refs.assignRole.roleIds = res.data.roleIds // 默认角色ID数组赋予给组件内roleIds影响页面默认选中

      // 为何我有2个默认的角色ID, 但是列表只勾选了1个呢? (问题是后台数据不一致性, 和前端 无关)
      // 原因: 此员工原来有角色1和角色2
      // 但是: 我们去管理角色列表数据时, 把角色2删除了, 但是员工在数据库里还有角色1和角色id
      // 分配角色列表获取的时候, 没有角色2, 所以没办法显示和默认选中 (数据不一致性问题)
      // 解决: 后端主导: 在删除角色的时候, 后端应该去判断, 此角色是否有员工还在使用, 如果有人在使用, 给前端提示不能删除角色 / 后端直接把相关员工的此角色一起从数据库移除掉
    },
    // 获取角色列表
    async getRoleListFn() {
      // 第一次请求先拿到总数total
      const res = await getRoleListAPI()
      // console.log(res)

      // 第二次发送拿到所有角色
      // 这是没招的情况: 如果要是后端可以配合, 可以告诉他不传参的时候, 把所有数据给我. 我要铺设列表
      const res2 = await getRoleListAPI({
        page: 1,
        pagesize: res.data.total
      })
      // console.log(res2)

      this.roleList = res2.data.rows
    },
    // 分配角色->保持更新
    async saveRoleFn(roleIds) {
      // roleIds: 角色id的数组
      const res = await saveRoleIdsAPI(this.clickEmpId, roleIds)
      console.log(res)
      this.roleShowDialog = false
    }
  }
}
</script>

<style lang="scss" scoped>
.staffPhoto {
  width: 100px;
  height: 100px;
  border-radius: 50%;
}
</style>
